// $codepro.audit.disable booleanMethodNamingConvention
/**
 * Project: Utilities <br>
 * Package: org.leumasjaffe.functional.lazy
 */
package org.leumasjaffe.functional.lazy;

import java.util.Collection;
import java.util.Comparator;

import org.leumasjaffe.functional.BinaryFunction;
import org.leumasjaffe.functional.Consumer;
import org.leumasjaffe.functional.Predicate;
import org.leumasjaffe.functional.UnaryFunction;
import org.leumasjaffe.functional.UnaryOperator;

import org.eclipse.jdt.annotation.NonNullByDefault;

import com.google.common.base.Optional;

/**
 * Class org.leumasjaffe.functional.lazy..LazyEval in file LazyEval.java <br>
 * 
 * @author leumasjaffe
 * @version Dec 2, 2013
 * Created on: Dec 2, 2013
 * 
 * @param <T>
 */
@NonNullByDefault
public interface LazyEval<T> {

	/**
	 * Maps our collection element-wise according to a function of the form T -> R
	 * @param mapping A function that maps objects of type T to objects of type R, it is generally
	 * assumed that the mapping is one-to-one, that is, for each input t, there is a unique output
	 * r, though not necessarily the other way around.
	 * @param <R> The destination type of the map
	 * @return A lazy evaluator that will apply the mapping, eventually...
	 */
	<R> LazyEval<R> map(UnaryFunction<? super T, ? extends R> mapping);
	
	/**
	 * Filters our collection, removing all elements that do not pass
	 * @param criteria A function of the form T -> boolean that is used to check each element
	 * of our collection for compliance.
	 * @return A lazy evaluator that will apply the filter, eventually...
	 */
	LazyEval<T> filter(Predicate<? super T> criteria);

	/**
	 * Applies some mutable operation to every element of our collection
	 * @param mutator A function that alters the internal state of 
	 * @return A lazy evaluator that will consume everything, eventually...
	 */
	LazyEval<T> forEach(Consumer<? super T> mutator);
	
	/**
	 * Applies an immutable operation to each element of our collection. 
	 * This is equivalent to calling map, but is included because map is 
	 * generally considered to be used for changing types
	 * @param transformer A function of the form T -> T.
	 * @return A lazy evaluator that will apply the mapping, eventually...
	 */
	LazyEval<T> forEach(UnaryOperator<T> transformer);
	
	/**
	 * Folds the contents of our collection in a leftwards direction. This is a terminal operation
	 * and so will immediately compute its result
	 * @param folder A function of the form S -> T -> S
	 * @param accum Our initial value
	 * @param <S> The accumulator and resulting type
	 * @return accum, or the result of applying accum = monad.apply(accum, elt) forall elt.
	 */
	<S> S foldLeft(BinaryFunction<S, T, S> folder, S accum);
	
	/**
	 * Folds the contents of our collection in a rightwards direction. This is a terminal operation
	 * and so will immediately compute its result
	 * @param folder A function of the form T -> S -> S
	 * @param accum Our initial value
	 * @param <S> The accumulator and resulting type
	 * @return accum, or the result of applying accum = monad.apply(elt, accum) forall elt.
	 */
	<S> S foldRight(BinaryFunction<T, S, S> folder, S accum);
	
	/**
	 * Checks that every element in our collection meets the specified criteria. 
	 * This is a terminal operation and so will immediately compute its result
	 * @param criteria A predicate the match against
	 * @return true iff every element matches
	 */
	boolean allMatch(Predicate<? super T> criteria);
	
	/**
	 * Checks that any element in our collection meets the specified criteria.
	 * This is a terminal operation and so will immediately compute its result
	 * @param criteria A predicate the match against
	 * @return true iff any element matches
	 */
	boolean anyMatch(Predicate<? super T> criteria);
	
	/**
	 * Checks that no element in our collection meets the specified criteria. 
	 * This is a terminal operation and so will immediately compute its result
	 * @param criteria A predicate the match against
	 * @return true iff no element matches
	 */
	boolean noneMatch(Predicate<? super T> criteria);
	
	/**
	 * A specialized folding method that computes the maximum value in our collection. 
	 * This is a terminal operation and so will immediately compute its result
	 * @param comparator
	 * @return The maximum value or {@link Optional#absent()} if the collection is empty
	 */
	Optional<T> max(Comparator<? super T> comparator);
	
	/**
	 * A specialized folding method that computes the minimum value in our collection. 
	 * This is a terminal operation and so will immediately compute its result
	 * @param comparator
	 * @return The minimum value or {@link Optional#absent()} if the collection is empty
	 */
	Optional<T> min(Comparator<? super T> comparator);
	
	/**
	 * A generalization of min and max that finds the object in our collection that is the most
	 * of some given quality. This is a terminal operation and so will immediately 
	 * compute its result
	 * @param cmp A comparison function (e.g '<')
	 * @return The value most in our comparison or {@link Optional#absent()} if the 
	 * collection is empty
	 */
	Optional<T> most(BinaryFunction<T, T, Boolean> cmp);
	
	/**
	 * Immediately evaluate the state of our lazy evaluation chain, computing the result.
	 * @return A collection of the evaluated values
	 */
	Collection<T> eval();

	/**
	 * Immediately evaluate the state of our lazy evaluation chain, computing the result.
	 * @param type The Class<R> that will generate the output collection instance
	 * @param <R> The type of collection to store the output in
	 * @return A collection of the evaluated values
	 */
	<R extends Collection<T>> R eval(Class<R> type);

	/**
	 * Immediately evaluate the state of our lazy evaluation chain, computing the result.
	 * @param returnCollection The collection to be written to
	 * @param <R> The type of collection to store the output in
	 * @return A collection of the evaluated values
	 */
	<R extends Collection<T>> R eval(R returnCollection);

}